/*
Hermes Core: General Purpose Mobile Agent Platform
Copyright (C) 2006 UNICAM - Università degli Studi di Camerino

Group leaders: Flavio Corradini and Emanuela Merelli

Developers: Ezio Bartocci, Diego Bonura, Rosario Culmone,
Davide Lorenzoli, Leonardo Mariani, Francesca Piersigilli, 
Lorenzo Scortichini, Leonardo Vito.

GNU Lesser Public License

This library is free software; you can redistribute it 
and/or modify it under the terms of the GNU Lesser General 
Public License as published by the Free Software Foundation, 
version 2.1 of the License.

This library is distributed in the hope that it will be useful, 
but WITHOUT ANY WARRANTY; without even the implied warranty of 
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
See the GNU Library General Public License for more details.

You should have received a copy of the GNU Library General Public 
License along with this library; if not, write to the
Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.

Emanuela Merelli
Dipartimento di Matematica ed Informatica
Università di Camerino
via Madonna delle Carceri, 62032 Camerino, Italy
E.Mail: emanuela.merelli@unicam.it
*/
package hermesV2.agent;

import java.lang.reflect.InvocationTargetException;

import hermesV2.Message;
import hermesV2.PlaceAddress;

import hermesV2.basicServices.communication.MessageQueue;
import hermesV2.basicServices.mobility.TransportFailedException;

/**
 * The User agent class represents the normal agent running into a place. It has
 * the possibility to move to communicate with other agent and to search other
 * service agents. Every user agent developed by an user must extend this class.
 * 
 * @author Diego Bonura
 */
public abstract class UserAgent extends Agent {

    /**
	 * 
	 */
	private static final long serialVersionUID = -8778794839902767070L;

	/** The agent has to be restarted after a migration. */
    public static final int RESTART = 1;

    /** The agent hat to be restarted after a clonation. */
    public static final int CLONE = 2;

    /** Contains the method to call after a correct migration. */
    protected String callback;

    /** Contains the child method to call after a correct clonation. */
    protected String cloneback;

    /** Contains the queue message to attach to the agent before moving */
    protected MessageQueue<Message<?>>  messageQueue;

    /**
     * The father agent identificator (after a clone request)
     */
    private Identificator fatherId = null;

    /**
     * Creates a new UserAgent object giving the name.
     * 
     * @param UserAgentName
     *            The name of the agent
     */
    public UserAgent(String UserAgentName) {
        super(UserAgentName);
    }

    /**
     * This method permits the migration of the agent.
     * 
     * @param receiverPlaceAddress
     *            This is the place address where to move.
     * @param callback
     *            This is the name of the method to execute to the
     *            receiverPlaceAddres when the migration is terminated.
     * 
     * @throws MigrationException
     *             This exception is throwed when something went wrong during
     *             the migration.
     */
    final public void move(PlaceAddress receiverPlaceAddress, String callback)
            throws MigrationException {
        this.callback = callback;

        if (basicServices.getMoveInterface() != null) {
            if (basicServices.getSecurityInterface() != null) {
                basicServices.getSecurityInterface().logoutAgent(
                        this.getIdentificator());
            }

            if (basicServices.getReceiveInterface() != null) {
                this.setMessageQueue(basicServices.getReceiveInterface()
                        .getAgentMessageQueue(this.getIdentificator()));
                deReception();
            }
            try {
                basicServices.getMoveInterface().go(receiverPlaceAddress, this);
            } catch (TransportFailedException tfe) {
                if (getMessageQueue() != null) {
                    basicServices.getReceiveInterface().setAgentReception(
                            this.getIdentificator(), getMessageQueue());
                }
                throw new MigrationException(tfe.getMessage());
            }
        } else {
            throw new MigrationException("No mobility services in this place");
        }
    }

    /**
     * Calls the cloneback method after a correct clonation.
     * 
     * @throws InvocationTargetException
     *             The method to call is not correct.
     * @throws IllegalAccessException
     *             The executing method does not have access to the definition
     *             of the specified class.
     * @throws NoSuchMethodException
     *             When the callback method cannot be found.
     * @throws ThreadDeath
     *             Used to stop the execution of the method.
     */
    final public void cloneback() throws InvocationTargetException,
            IllegalAccessException, NoSuchMethodException, ThreadDeath {
        executeMethod(cloneback, null, null);
    }

    /**
     * Calls the callback method after a correct migration.
     * 
     * @throws InvocationTargetException
     *             The method to call is not correct.
     * @throws IllegalAccessException
     *             The executing method does not have access to the definition
     *             of the specified class.
     * @throws NoSuchMethodException
     *             When the callback method cannot be found.
     * @throws ThreadDeath
     *             Used to stop the execution of the method.
     */
    final public void callback() throws InvocationTargetException,
            IllegalAccessException, NoSuchMethodException, ThreadDeath {
        //I have tho check the message queue
        if (getMessageQueue()!=null){
            basicServices.getReceiveInterface().setAgentReception(
                    this.getIdentificator(), getMessageQueue());
        }
        executeMethod(callback, null, null);
    }

    /**
     * This method permites the clonation of an user agent. The new agent will
     * have a new identificator and the first method call will be cloneback.
     * 
     * @param cloneback
     *            The name of the method to call after the clonation.
     * @param agentName
     *            The name of the cloned agent.
     * 
     * @return The identificator of the agent cloned.
     * 
     * @throws CloneException
     *             This exception will be throwed when something went wrong
     *             during the clonation phase.
     */
    final public Identificator clone(String cloneback, String agentName)
            throws CloneException {
        this.cloneback = cloneback;

        Identificator idCloneAgent = null;

        if (basicServices.getGenesisInterface() != null) {
            idCloneAgent = basicServices.getGenesisInterface().cloneAgent(this,
                    agentName);
        } else {
            throw new CloneException("No genesis services in this place");
        }

        return idCloneAgent;
    }

    /**
     * This method permites the clonation of an user agent. The new agent will
     * have a new identificator and the first method call will be cloneback.
     * 
     * @param cloneback
     *            The name of the method to call after the clonation.
     * 
     * 
     * @return The identificator of the agent cloned.
     * 
     * @throws CloneException
     *             This exception will be throwed when something went wrong
     *             during the clonation phase.
     */
    final public Identificator clone(String cloneback) throws CloneException {
        return clone(cloneback, this.getAgentName());
    }

    /**
     * Returns the identificator of the father agent.
     * 
     * @return The identificator of the father agent.
     */
    final public Identificator getFatherIdentificator() {
        return fatherId;
    }

    /**
     * The place uses this method to set automatically the father identificator
     * to every cloned agent.
     * 
     * @param fatherId
     *            The father agent identificator generated by the place (Id
     *            component).
     */
    final public void setFatherIdentificator(Identificator fatherId) {
        this.fatherId = fatherId;
    }

    /**
     * The place uses this method to set automatically the message queue before
     * moving the agent.
     * 
     * 
     * @param messageQueue
     *            The message queue of the agent could be null.
     */
    final private void setMessageQueue(MessageQueue<Message<?>>  messageQueue) {
        this.messageQueue = messageQueue;
    }

    /**
     * The place uses this method to get automatically the message queue after
     * moving the agent.
     * 
     * 
     * @return The message queue of the agent could be null.
     */
    final private MessageQueue<Message<?>> getMessageQueue() {
        return messageQueue;
    }
}